﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CalcClass
{
    /// <summary>
    /// 用于计算研五标准片的荧光计算算法
    /// </summary>
    internal class FluoCalcMethod
    {        
        private List<double> fluodata; //定义了该类的私有字段
        public List<double> FluoData
        {
            set { fluodata = value; }
        }

        public string[] CalFluo()
        {
            try
            {
                var dataCount = fluodata.Count;
                var point = dataCount / 20; //var point = 9;
                var cx = 0;
                var tx = 0;
                double cy = 0;
                double ty = 0;
                
                var tcspan = 90; //研五的标准片的tcspan大概是90左右
                //目前取值可能范围在72-96，故最佳取值在84
                //根据研四的数据，选取90，并不是最佳，我的建议是80 or 85

                int deltaX = 4;
                int slopeLen = dataCount - deltaX;
                List<double> slope = new List<double>(); //计算斜率
                for (int startPoint = 0; startPoint < slopeLen; startPoint++) //startPoint=0~175
                {
                    slope.Add((fluodata[startPoint + deltaX] - fluodata[startPoint]) / deltaX);
                }

                #region 分开高低浓度，并各自检峰
                //接下来生成两个指标
                var index = 1; //指标1：查看曲线是否有明显的峰
                var delta = 0.35; //指标2：max(max-mean)                
                //如果进入低浓度判断则进行一下赋值
                if (slope.Max()*10<index && fluodata.Max()-GetArg(fluodata)<delta)
                {
                    int lower = 44;
                    int upper = 64; //43~63
                    for (int x = lower-1; x <= upper-1; x++)
                    {
                        if (fluodata[x] > cy)
                        {
                            cx = x;
                            cy = fluodata[x];
                        }
                    }
                    for (int x = cx + tcspan - 2 * point; x <= cx + tcspan + 2 * point; x++)
                    {
                        if (fluodata[x] > ty)
                        {
                            tx = x;
                            ty = fluodata[x];
                        }
                    }
                }
                else //其他浓度的计算
                {
                    //for (var i = dataCount / 10; i < dataCount * 9 / 10; i++)
                    for (var i = point; i < dataCount - point; i++)
                    //此处是我增加，可以取得更宽的范围
                    {
                        if (fluodata[i] > cy) //出现平峰会取左侧最大值
                        {
                            cx = i;
                            cy = fluodata[i];
                        }
                    }

                    #region //正常检峰过程
                    //if (cx > dataCount / 2 & cy >= fluodata[cx + 1])
                    if (cx >= dataCount / 2 && cy >= fluodata[cx + 1]) //cx=90开始，实际上是第91个点
                    {
                        tx = cx;
                        ty = cy;
                        cx = 0;
                        cy = 0;
                        //for (var i = Math.Max(0, tx - tcspan - 2 * point); i < tx - tcspan + 2 * point; i++)
                        for (var i = Math.Max(point + 1, tx - tcspan - 2 * point); i < Math.Min(dataCount / 2, tx - tcspan + 2 * point); i++)
                        {
                            if (fluodata[i] > cy)
                            {
                                cy = fluodata[i];
                                cx = i;
                            }
                        }

                        //if (cx == Math.Max(0, tx - tcspan - 2 * point) | cx == tx - tcspan + 2 * point - 1)
                        if (cx == Math.Max(point + 1, tx - tcspan - 2 * point) | cx == Math.Min(dataCount / 2, tx - tcspan + 2 * point))
                        {
                            //cx = tx - tcspan;
                            //cy = fluodata[cx];
                            //我们应该在更小的范围内搜索，此时有一种可能是基底上扬的高度比很矮的峰还要高，所以可以在更小的范围内搜索
                            //至少可以更小限度的减小误差。
                            cx = 0;
                            cy = 0;
                            for (var i = Math.Max(point + 1, tx - tcspan - point); i < Math.Min(dataCount / 2, tx - tcspan + point); i++)
                            {
                                if (fluodata[i] > cy)
                                {
                                    cx = i;
                                    cy = fluodata[i];
                                }
                            }
                        }
                    }

                    else if (cx < dataCount / 2 & cy >= fluodata[cx - 1])
                    {
                        //for (var i = cx + tcspan - 2 * point; i < Math.Min(cx + tcspan + 2 * point, fluodata.Count); i++)
                        for (var i = Math.Max(dataCount / 2, cx + tcspan - 2 * point); i < Math.Min(cx + tcspan + 2 * point, fluodata.Count - point); i++)
                        {
                            if (fluodata[i] > ty)
                            {
                                ty = fluodata[i];
                                tx = i;
                            }
                        }

                        //if (cx == tx - tcspan - 2 * point | cx == Math.Min(cx + tcspan + 2 * point, fluodata.Count) - 1)
                        if (tx == Math.Max(dataCount / 2, cx + tcspan - 2 * point) | tx == Math.Min(cx + tcspan + 2 * point, fluodata.Count - point) - 1)
                        {
                            //tx = cx + tcspan;
                            //ty = fluodata[tx];
                            tx = 0;
                            ty = 0;
                            for (var i = Math.Max(dataCount / 2, cx + tcspan - point); i < Math.Min(cx + tcspan + point, fluodata.Count - point); i++)
                            {
                                if (fluodata[i] > ty)
                                {
                                    tx = i;
                                    ty = fluodata[i];
                                }
                            }
                        }
                    }
                    #endregion //检峰过程
                }
                #endregion 

                double sumC = 0;
                double sumT = 0;
                for (var i = -point; i < point; i++) //共18个点
                {
                    sumC += fluodata[cx + i];
                    sumT += fluodata[tx + i];
                }
                

                #region 计算四个基底的面积
                double base1 = 0;
                double base2 = 0;
                double base3 = 0;
                double base4 = 0;
                for (var i = cx + point * 2; i < cx + point * 3; i++)
                {
                    base2 += fluodata[i];
                }
                for (var i = tx - point * 3; i < tx - point * 2; i++)
                {
                    base3 += fluodata[i];
                }

                if (cx - point * 2 - Math.Max(0, cx - point * 3) < 1)
                {
                    base1 = base2;
                }
                else
                {
                    for (var i = Math.Max(0, cx - point * 3); i < cx - point * 2; i++)
                    {
                        base1 += fluodata[i];
                    }
                    base1 = base1 / (cx - point * 2 - Math.Max(0, cx - point * 3)) * point;
                }

                if (Math.Min(dataCount - 1, tx + point * 3) - tx - point * 2 < 1)
                {
                    base4 = base3;
                }
                else
                {
                    for (var i = tx + point * 2; i < Math.Min(dataCount - 1, tx + point * 3); i++)
                    {
                        base4 += fluodata[i];
                    }
                    base4 = base4 / (Math.Min(dataCount - 1, tx + point * 3) - tx - point * 2) * point;
                }
                #endregion //计算四个基底的面积

                var sumCBase = sumC - base1 - base2;
                var sumTBase = sumT - base3 - base4;

                if (slope.Max() * 10 < index && fluodata.Max() - GetArg(fluodata) < delta)
                {
                    double sumTemp = 0;
                    double argTemp = 0;
                    int nTemp = 0;
                    for (int i = Math.Max(0,cx-20); i < cx+20; i++) //刨掉C峰可能位置的40个点
                    {
                        sumTemp += fluodata[i];
                    }
                    for (int i = tx-20; i < Math.Min(dataCount,tx+20); i++) //刨掉T峰可能位置的40个点
                    {
                        sumTemp += fluodata[i];
                    }
                    //为何nTemp要写成这样，假设tx=160，就会明白，差了一个1.
                    nTemp = dataCount - (cx + 19 - Math.Max(0, cx - 20) +1) - (Math.Min(dataCount-1, tx + 19) - (tx - 20) +1);
                    argTemp = (fluodata.Sum() - sumTemp) / nTemp; //求解剩余100个点的均值
                    sumCBase = sumC - 18 * argTemp;
                    sumTBase = sumT - 18 * argTemp;
                }

                var ii = 0.5; //指标，对于负值，想赋值的范围
                if (sumCBase < 0)
                {
                    sumCBase = Math.Abs(sumCBase);
                    while (sumCBase > ii)
                    {
                        sumCBase = sumCBase / 2;
                    }
                }
                if (sumTBase < 0)
                {
                    sumTBase = Math.Abs(sumTBase);
                    while (sumTBase > ii)
                    {
                        sumTBase = sumTBase / 2;
                    }
                }

                /*
                var ii = 1;
                while (sumCBase < 0 & ii < 11)
                {
                    //sumCBase += (Math.Max(base2, base1) - Math.Min(base2, base1)) * ii / 10;
                    //如果base1==base2，那么sumCBase依然是负数，没有效果；
                    if (base1 != base2)
                    {
                        sumCBase += Math.Abs(sumC - base2 * 2) * ii / 10;
                    }
                    sumCBase += Math.Abs(sumC - base2 *2) * ii / 10;
                    ii++;
                }
                ii = 1;
                while (sumTBase < 0 & ii < 11)
                {
                    //sumTBase += (Math.Max(base4, base3) - Math.Min(base4, base3)) * ii / 10;
                    //问题同上
                    sumTBase += Math.Abs(sumT - base3 *2) * ii / 10; //还是会出问题
                    ii++;
                }
                */

                var Tap = sumTBase / (sumTBase + sumCBase) * 5000;

                string[] strOd = new string[11];
                string floatstr = "F4";
                strOd[0] = cx.ToString();
                strOd[1] = cy.ToString(floatstr);
                strOd[2] = tx.ToString();
                strOd[3] = ty.ToString(floatstr);

                strOd[4] = base1.ToString(floatstr);
                strOd[5] = base2.ToString(floatstr);
                strOd[6] = base3.ToString(floatstr);
                strOd[7] = base4.ToString(floatstr);

                strOd[8] = sumCBase.ToString(floatstr);
                strOd[9] = sumTBase.ToString(floatstr);
                strOd[10] = Tap.ToString(floatstr);

                //var strOd = "C(%1, %2); T(%3, %4); base1(%5); base2(%6); base3(%7); base4(%8); SumCBase(%9); ";
                //strOd = strOd.Replace("%1", cx.ToString());
                //strOd = strOd.Replace("%2", cy.ToString("F4"));
                //strOd = strOd.Replace("%3", tx.ToString());
                //strOd = strOd.Replace("%4", ty.ToString("F4"));

                //strOd = strOd.Replace("%5", base1.ToString("F4"));
                //strOd = strOd.Replace("%6", base2.ToString("F4"));
                //strOd = strOd.Replace("%7", base3.ToString("F4"));
                //strOd = strOd.Replace("%8", base4.ToString("F4"));

                //strOd = strOd.Replace("%9", sumCBase.ToString());
                ////strOd = strOd.Replace("%10", sumTBase.ToString());
                //var SumTBase = sumTBase.ToString("F4");
                //string TAP = Tap.ToString("F4");
                ////strOd = strOd.Replace("%11", TAP);
                //strOd = strOd + "SumTBase(" + SumTBase + "); Tap(" + TAP +")";
                return strOd;
            }
            catch (Exception)
            {
                string[] strOd = new string[1];
                strOd[0] = "Error:非正常测试片";
                return strOd;
            }
        }

        private double GetArg(List<double> data)
        {
            double sum = 0;
            foreach (double item in data)
            {
                sum += item;
            }
            return sum / data.Count;
        }
    }
}
